package com.android.launcher3;

import java.util.ArrayList;
import java.util.List;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;

/**
 *  Stores the list of all applications for the all apps view.
 */
class AllAppsList {

	public static final int DEFAULT_APPLICATIONS_NUMBER = 42;

	/** The list off all apps. */
	public ArrayList<ApplicationInfo> data = new ArrayList<ApplicationInfo>(
			DEFAULT_APPLICATIONS_NUMBER);

	/** The list of apps that have been added since the last notify() call. */
	public ArrayList<ApplicationInfo> added = new ArrayList<ApplicationInfo>(
			DEFAULT_APPLICATIONS_NUMBER);

	/** The list of apps that have been removed since the last notify() call. */
	public ArrayList<ApplicationInfo> removed = new ArrayList<ApplicationInfo>();

	/** The list of apps that have been modified since the last notify() call. */
	public ArrayList<ApplicationInfo> modified = new ArrayList<ApplicationInfo>();

	private IconCache mIconCache;

	/**
	 * Boring constructor.
	 */
	public AllAppsList(IconCache iconCache) {
		mIconCache = iconCache;
	}

	/**
	 * Add the supplied ApplicationInfo objects to the list, and enqueue it into
	 * the list to broadcast when notify() is called. If the app is already in
	 * the list, doesn't add it.
	 * 
	 * @param info
	 */
	public void add(ApplicationInfo info) {
		if (findActivity(data, info.componentName)) {
			return;
		}

		data.add(info);
		added.add(info);
	}

	public void clear() {
		data.clear();
		// TODO: do we clear these too?
		added.clear();
		removed.clear();
		modified.clear();
	}

	public int size() {
		return data.size();
	}

	public ApplicationInfo get(int index) {
		return data.get(index);
	}

	/**
	 * Add the icons for the supplied apk called packageName.
	 * 
	 * @param context
	 * @param packageName
	 */
	public void addPackage(Context context, String packageName) {
		final List<ResolveInfo> matches = findActivitiesForPackage(context,
				packageName);

		if (matches.size() > 0) {
			for (ResolveInfo info : matches) {
				add(new ApplicationInfo(info, mIconCache));
			}
		}
	}

	/**
	 * Remove the apps for the given apk identified by packageName.
	 * 
	 * @param packageName
	 */
	public void removePackage(String packageName) {
		final List<ApplicationInfo> data = this.data;

		for (int i = data.size() - 1; i >= 0; i--) {
			ApplicationInfo info = data.get(i);
			final ComponentName component = info.intent.getComponent();

			if (packageName.equals(component.getPackageName())) {
				removed.add(info);
				data.remove(i);
			}
		}

		// This is more aggressive than it needs to be.
		mIconCache.flush();
	}

	/**
	 * Add and remove icons for this package which has been updated.
	 * 
	 * @param context
	 * @param packageName
	 */
	public void updatePackage(Context context, String packageName) {
		final List<ResolveInfo> matches = findActivitiesForPackage(context,
				packageName);

		if (matches.size() > 0) {
			// Find disabled/removed activities and remove them from data and
			// add them
			// to the removed list.
			for (int i = data.size() - 1; i >= 0; i--) {
				final ApplicationInfo applicationInfo = data.get(i);
				final ComponentName component = applicationInfo.intent
						.getComponent();

				if (packageName.equals(component.getPackageName())) {
					if (!findActivity(matches, component)) {
						removed.add(applicationInfo);
						mIconCache.remove(component);
						data.remove(i);
					}
				}
			}

			// Find enabled activities and add them to the adapter
			// Also updates existing activities with new labels/icons

			// 贺辉修改 2011年11月8日16:04:58
			// int count = matches.size();

			// for (int i = 0; i < count; i++) {
			// final ResolveInfo info = matches.get(i);
			// ApplicationInfo applicationInfo =
			// findApplicationInfoLocked(info.activityInfo.applicationInfo.packageName,
			// info.activityInfo.name);
			//
			// if (applicationInfo == null) {
			// add(new ApplicationInfo(info, mIconCache));
			// } else {
			// mIconCache.remove(applicationInfo.componentName);
			// mIconCache.getTitleAndIcon(applicationInfo, info);
			// modified.add(applicationInfo);
			// }
			// }
			//
			// 修改结束

			for (ResolveInfo info : matches) {
				ApplicationInfo applicationInfo = findApplicationInfoLocked(
						info.activityInfo.applicationInfo.packageName,
						info.activityInfo.name);

				if (applicationInfo == null) {
					add(new ApplicationInfo(info, mIconCache));
				} else {
					mIconCache.remove(applicationInfo.componentName);
					mIconCache.getTitleAndIcon(applicationInfo, info);
					modified.add(applicationInfo);
				}

			}

		} else {
			// Remove all data for this package.
			for (int i = data.size() - 1; i >= 0; i--) {
				final ApplicationInfo applicationInfo = data.get(i);
				final ComponentName component = applicationInfo.intent
						.getComponent();

				if (packageName.equals(component.getPackageName())) {
					removed.add(applicationInfo);
					mIconCache.remove(component);
					data.remove(i);
				}
			}
		}
	}

	/**
	 * Query the package manager for MAIN/LAUNCHER activities in the supplied
	 * package.
	 * 
	 * @param context
	 * @param packageName
	 * 
	 * @return
	 */
	private static List<ResolveInfo> findActivitiesForPackage(Context context,
			String packageName) {
		final PackageManager packageManager = context.getPackageManager();

		final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
		mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
		mainIntent.setPackage(packageName);

		final List<ResolveInfo> apps = packageManager.queryIntentActivities(
				mainIntent, 0);

		return (apps != null) ? apps : new ArrayList<ResolveInfo>();
	}

	/**
	 * Returns whether <em>apps</em> contains <em>component</em>.
	 * 
	 * @param apps
	 * @param component
	 * 
	 * @return
	 */
	private static boolean findActivity(List<ResolveInfo> apps,
			ComponentName component) {
		final String className = component.getClassName();

		for (ResolveInfo info : apps) {
			final ActivityInfo activityInfo = info.activityInfo;

			if (activityInfo.name.equals(className)) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Returns whether <em>apps</em> contains <em>component</em>.
	 * 
	 * @param apps
	 * @param component
	 * 
	 * @return
	 */
	private static boolean findActivity(ArrayList<ApplicationInfo> apps,
			ComponentName component) {
		for (ApplicationInfo info : apps) {
			if (info.componentName.equals(component)) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Find an ApplicationInfo object for the given packageName and className.
	 * 
	 * @param packageName
	 * @param className
	 * 
	 * @return
	 */
	private ApplicationInfo findApplicationInfoLocked(String packageName,
			String className) {
		for (ApplicationInfo info : data) {
			final ComponentName component = info.intent.getComponent();

			if (packageName.equals(component.getPackageName())
					&& className.equals(component.getClassName())) {
				return info;
			}
		}

		return null;
	}
}
